﻿/*
 * RETRO 2012 - v2.3
 * 
 * PaRADIIT Project
 * https://sites.google.com/site/paradiitproject/
 * 
 * This software is provided under LGPL v.3 license, 
 * which exact definition can be found at the following link:
 * http://www.gnu.org/licenses/lgpl.html
 * 
 * Please, contact us for any offers, remarks, ideas, etc.
 * 
 * Copyright © RFAI, LI Tours, 2011-2012
 * Contacts : rayar@univ-tours.fr
 *            ramel@univ-tours.fr
 * 
 */

using System;
using System.Xml.Linq;
using System.Text;
using System.IO;
using System.Windows;
using System.ComponentModel;
using System.Collections.Generic;

namespace Retro.Model.core
{
    /// <summary>
    /// Represent a Cluster
    /// </summary>
    [Serializable]
    public class Cluster : INotifyPropertyChanged
    {

        #region Attributes

        private String _ClusterId;
        /// <summary>
        /// ID of the cluster
        /// </summary>
        public String ClusterId
        {
            get { return _ClusterId; }
            set
            {
                _ClusterId = value;
                NotifyPropertyChanged("ClusterId");
            }
        }


        private bool _IsLabelized;
        /// <summary>
        /// Indicate if the cluster has been labelized
        /// </summary>
        public bool IsLabelized
        {
            get { return _IsLabelized; }
            set
            {
                _IsLabelized = value;
                NotifyPropertyChanged("IsLabelized");
            }
        }


        private String _ClusterFilepath;
        /// <summary>
        /// File associated to this of the cluster 
        /// </summary>
        public String ClusterFilepath
        {
            get { return _ClusterFilepath; }
            set
            {
                _ClusterFilepath = value;
                NotifyPropertyChanged("ClusterFilepath");
            }
        }


        private int _NbShapes;
        /// <summary>
        /// Number of the shapes of this cluster 
        /// </summary>
        public int NbShapes
        {
            get { return _NbShapes; }
            set
            {
                _NbShapes = value;
                NotifyPropertyChanged("NbShapes");
            }
        }


        private List<Shape> _ShapesList = new List<Shape>();
        /// <summary>
        /// Shapes List 
        /// </summary>
        public List<Shape> ShapesList
        {
            get { return _ShapesList; }
            set
            {
                _ShapesList = value;
                NotifyPropertyChanged("ShapesList");
            }
        }


        private String _RepresentativePath;
        /// <summary>
        /// Path of the representative of this cluster
        /// </summary>
        public String RepresentativePath
        {
            get { return _RepresentativePath; }
            set
            {
                _RepresentativePath = value;
                NotifyPropertyChanged("RepresentativePath");
            }
        }


        private Shape _Representative;
        /// <summary>
        /// Representative of this cluster
        /// </summary>
        public Shape Representative
        {
            get { return _Representative; }
            set
            {
                _Representative = value;
                NotifyPropertyChanged("Representative");
            }
        }


        private List<String> _LabelList = new List<String>();
        /// <summary>
        /// Label List 
        /// </summary>
        public List<String> LabelList
        {
            get { return _LabelList; }
            set
            {
                _LabelList = value;
                NotifyPropertyChanged("LabelList");
            }
        }


        private List<String> _MethodList = new List<String>();
        /// <summary>
        /// Method List used for the labeling
        /// </summary>
        public List<String> MethodList
        {
            get { return _MethodList; }
            set
            {
                _MethodList = value;
                NotifyPropertyChanged("MethodList");
            }
        }


        private List<Double> _ConfidenceList = new List<Double>();
        /// <summary>
        /// Confidence rate for the labling
        /// </summary>
        public List<Double> ConfidenceList
        {
            get { return _ConfidenceList; }
            set
            {
                _ConfidenceList = value;
                NotifyPropertyChanged("ConfidenceList");
            }
        }


        #endregion

        #region Constructors

        /// <summary>
        /// Default Constructor
        /// </summary>
        public Cluster()
        {
        }


        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="filepath">Path of the ClusterXXXX xml file</param>
        /// <param name="shapeDir">Path of the png shapes</param>
        /// <param name="altoDir">path of the alto file</param>
        public Cluster(String filepath, String shapeDir, String altoDir)
        {
            // Check if the file exists
            if (!File.Exists(filepath))
            {
                MessageBox.Show("ERROR: " + filepath + " doesn't exist", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
            else
            {
                // Assign cluster parameters
                this._ClusterFilepath = filepath;

                // Assign ID (regarding filename)
                String filename = Path.GetFileNameWithoutExtension(filepath);
                filename = filename.Substring(7);
                this._ClusterId = filename;

                // Load the cluster xml file
                XDocument xmlDoc = XDocument.Load(filepath);

                // Assign NbShapes
                XElement xCluster = xmlDoc.Element("cluster");
                this._NbShapes = Convert.ToInt32(xCluster.Attribute("size").Value);
                
                // Assign label if exist
                String label = xCluster.Attribute("label").Value;
                if (label.CompareTo("") != 0)
                {
                    this.AddNewLabel("CLUSTERING", label, 0.85);
                    this.IsLabelized = true;
                }

                // Fill the cluster with its Shapes
                Shape currentshape;
                foreach (XElement xCC in xmlDoc.Descendants("cc"))
                {
                    String ccID = xCC.Attribute("id").Value;
                    currentshape = new Shape(ccID, this._ClusterId, shapeDir, altoDir);
                    this._ShapesList.Add(currentshape);
                }

                this._Representative = this._ShapesList[0];

            }
        }


        #endregion


        /// <summary>
        /// Insert a new label for the current cluster 
        /// </summary>
        /// <param name="methodname">method of the labelling</param>
        /// <param name="label">New label</param>
        /// <param name="confidenceRate">Confidence of the new label, in [0,1]</param>
        public void AddNewLabel(String methodname, String label, Double confidenceRate)
        {
            // Get the correct insert index regarding condifance rate DESC!
            int index = 0;
            while ((index < this._ConfidenceList.Count) && (confidenceRate < this._ConfidenceList[index]))
                index++;

            // Add a the end or Insert methodname, label, and confidence in the 3 List at the computed index
            if (index == this._ConfidenceList.Count)
            {
                this._MethodList.Add(methodname);
                this._LabelList.Add(label);
                this._ConfidenceList.Add(confidenceRate);
            }
            else
            {
                this._MethodList.Insert(index, methodname);
                this._LabelList.Insert(index, label);
                this._ConfidenceList.Insert(index, confidenceRate);
            }

            if (methodname.CompareTo("DEFAULT") != 0)
                this.UpdateAltoFile(label);

        }


        /// <summary>
        /// Update the label attribute in the cluster xml file regarding the new assigned label
        /// </summary>
        /// <param name="newLabel">New label</param>
        private void UpdateAltoFile(String newLabel)
        {
            // Load the cluster xml file
            XDocument xmlDoc = XDocument.Load(this._ClusterFilepath);
            XElement clusterElement = xmlDoc.Element("cluster");

            if (clusterElement != null)
                clusterElement.Attribute("label").Value = newLabel;

            xmlDoc.Save(this._ClusterFilepath);
        }


        /// <summary>
        /// Clear class lists
        /// </summary>
        public void Reset()
        {
            this._ShapesList.Clear();
            this._ShapesList = null;

            this._LabelList.Clear();
            this._LabelList = null;

            this._MethodList.Clear();
            this._MethodList = null;

            this._ConfidenceList.Clear();
            this._ConfidenceList = null;
        }


        /// <summary>
        /// For Display purpose in GUI ListBox
        /// </summary>
        public override String ToString()
        {
            String description = "ID\t\t" + this._ClusterId + "\n"
                + "Nb Shapes\t" + this._NbShapes + "\n"
                + "Transcription\t";

            description += (this._IsLabelized) ? this._LabelList[0] : "-";

            return description;
        }


        /// <summary>
        /// For binding purpose
        /// </summary>
        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// For binding purpose
        /// </summary>
        private void NotifyPropertyChanged(String propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

    }
}
